/*
 * Copyright (c) 2005, 2014 vacoor
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 */
package org.vacoor.nothing.security.nls.support;

import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableMap;
import org.vacoor.nothing.common.json.Jacksons;
import org.vacoor.nothing.common.util.Dates;
import org.vacoor.nothing.common.util.HttpUtils;
import org.vacoor.nothing.common.util.IOUtils;
import org.vacoor.nothing.common.util.StringUtils;
import org.vacoor.nothing.security.nls.TranslationException;
import org.vacoor.nothing.security.nls.Translator;

import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.Locale;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

/**
 * https://msdn.microsoft.com/en-us/library/ff512406.aspx
 * 如果没有 APP ID 可以使用如下方式获取 Client ID, Client Secret
 * https://datamarket.azure.com/developer/applications/
 *
 * @author vacoor
 */
public class BingTranslator extends TranslatorSupport {
    private static final String BING_API = "http://api.microsofttranslator.com/V2/Ajax.svc/TranslateArray";
    public static final String BING_APP_ID_PROP = "bing.app.id";
    public static final String BING_APP_CLIENT_ID_PROP = "bing.app.client_id";
    public static final String BING_APP_CLIENT_SECRET_PROP = "bing.app.client_secret";

    public static final Translator INSTANCE = new BingTranslator();

    @Override
    public boolean accepts(Locale from, Locale to) {
        return true;
    }

    @Override
    protected String doTranslate(String text, Locale from, Locale to, Properties props) {
        LOG.debug("执行必应翻译: {}, {} -> {}", text, from, to);
        return doTranslateInternal(new String[]{text}, from, to, props)[0];
    }

    protected String[] doTranslateInternal(String[] texts, Locale from, Locale to, Properties props) {
        if (1 > texts.length) {
            return texts;
        }

        String appId = props.getProperty(BING_APP_ID_PROP);
        if (!StringUtils.hasText(appId)) {
            String clientId = props.getProperty(BING_APP_CLIENT_ID_PROP);
            String clientSecret = props.getProperty(BING_APP_CLIENT_SECRET_PROP);
            if (!StringUtils.hasText(clientId) || !StringUtils.hasText(clientSecret)) {
                throw new TranslationException(BING_APP_ID_PROP + " or ( " + BING_APP_CLIENT_ID_PROP + " and " + BING_APP_CLIENT_SECRET_PROP + " ) must be set one");
            }
            // 没有设置 app id 但是存在 client_id, client_secret
            appId = requestAppId(clientId, clientSecret);
        }
        // appId = "TRnN8Db_l73Fb6wn5er_wuw9Tqst3frlDJ0Mti0D5x2g*";

        String[] result = null;
        try {
            InputStream is = HttpUtils.get(BING_API, ImmutableMap.<String, Object>of(
                    "appId", appId,
                    "from", toDash(from),
                    "to", toDash(to),
                    "texts", toLiteral(texts)
            ), 1000);

            String response = IOUtils.readText(is, Charsets.UTF_8, true);
            // 去除 BOM 头
            response = response.replaceAll("^(?:\uFEFF)?", "");
            if (!response.startsWith("[")) {
                throw new TranslationException(response);
            }

            JsonNode root = Jacksons.deserialize(response, JsonNode.class);
            result = new String[root.size()];
            for (int i = 0; i < result.length; i++) {
                result[i] = root.get(i).get("TranslatedText").asText();
            }
        } catch (IOException e) {

        }
        return result;
    }

    private String toLiteral(String[] texts) {
        StringBuilder buff = new StringBuilder();
        buff.append("[");
        for (int i = 0; i < texts.length; i++) {
            buff.append(i > 0 ? ",\"" : "\"").append(texts[i]).append("\"");
        }
        buff.append("]");
        return buff.toString();
    }

    static Date expiresDate;
    static String appId;

    private static synchronized String requestAppId(final String clientId, final String clientSecret) {
        try {
            // http://blogs.msdn.com/b/translation/p/gettingstarted1.aspx
            final Date now = Dates.now();
            if (null == expiresDate || now.after(expiresDate)) {
                String url = "https://datamarket.accesscontrol.windows.net/v2/OAuth2-13";
                InputStream is = HttpUtils.post(url, ImmutableMap.<String, Object>of(
                        "grant_type", "client_credentials",
                        "client_id", clientId,
                        "client_secret", clientSecret,
                        "scope", "http://api.microsofttranslator.com"
                ), 1000);
                JsonNode root = Jacksons.deserialize(is, JsonNode.class);
                String access_token = root.get("access_token").asText();
                int expires_in = root.get("expires_in").asInt(0);

                expiresDate = Dates.after(now, expires_in, TimeUnit.SECONDS);
                appId = "Bearer " + access_token;
            }
        } catch (IOException e) {
            expiresDate = null;
        }
        return appId;
    }
}
